﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;

namespace XYAuto.ChiTu.Excel
{
    public class ExcelHelper<T> where T : class
    {
        private T _exportDto;
        private DataTable _dt;
        private Dictionary<string, PropertyInfo> _propertyInfos;

        public ExcelHelper()
        {
            BuildTableSchma();
        }

        public ExcelHelper(T dto)
        {
            _exportDto = dto;
            BuildTableSchma(_exportDto);
        }

        #region 导入

        /// <summary>
        /// 从Stream流中导入到List
        /// </summary>
        /// <param name="fileStream">文件流</param>
        /// <param name="fileName">文件名称</param>
        /// <returns></returns>
        public IEnumerable<T> GetListFromStream(Stream fileStream, string fileName)
        {
            var isCompatible = GetIsCompatible(fileName);
            DataTable dataTable = NpoiExcelHelper.ReadExcelToDataTable(fileStream, isCompatible);
            return PopularListFromDt(dataTable);
        }

        /// <summary>
        /// 从Stream流中导入到List
        /// </summary>
        /// <param name="fileStream"></param>
        /// <param name="fileName"></param>
        /// <param name="sheetName"></param>
        /// <returns></returns>
        public IEnumerable<T> GetListFromStream(Stream fileStream, string fileName, string sheetName)
        {
            var isCompatible = GetIsCompatible(fileName);
            DataTable dataTable = NpoiExcelHelper.ReadExcelToDataTable(fileStream, isCompatible, sheetName);
            return PopularListFromDt(dataTable);
        }

        /// <summary>
        /// 从文件中导入到List
        /// </summary>
        /// <param name="filePath">文件地址</param>
        /// <returns></returns>
        public IEnumerable<T> GetListFromFile(string filePath)
        {
            if (!File.Exists(filePath))
                throw new ArgumentException(string.Format("要转化的EXCEL文件地址不存在:{0}", filePath));
            DataTable dataTable = NpoiExcelHelper.ReadExcelToDataTable(filePath, GetIsCompatible(filePath));
            return PopularListFromDt(dataTable);
        }

        public IEnumerable<T> GetListFromFile(string filePath, string sheetName)
        {
            if (!File.Exists(filePath))
                throw new ArgumentException(string.Format("要转化的EXCEL文件地址不存在:{0}", filePath));
            DataTable dataTable = NpoiExcelHelper.ReadExcelToDataTable(filePath, GetIsCompatible(filePath), sheetName);
            return PopularListFromDt(dataTable);
        }

        #endregion 导入

        #region 导出

        /// <summary>
        /// 返回excel格式的内存流
        /// </summary>
        /// <param name="list">要导出的数据源</param>
        /// <param name="headMsg"></param>
        /// <returns></returns>
        public MemoryStream GetExcelMemoryFromList(IEnumerable<T> list, string headMsg = "")
        {
            var ms = NpoiExcelHelper.ExportExcelToMeory(list, false, "sheet1", _propertyInfos, headMsg);
            return ms;
        }

        /// <summary>
        /// 将IEnumerable保存到指定的地址
        /// </summary>
        /// <param name="list">数据列表</param>
        /// <param name="dirPath"></param>
        /// <param name="fileName"></param>
        /// <param name="headMsg"></param>
        public void SaveExcelToFile(IEnumerable<T> list, string dirPath, string fileName, string headMsg = "")
        {
            if (string.IsNullOrEmpty(dirPath))
                throw new ArgumentException("文件地址不能为空");
            if (string.IsNullOrEmpty(fileName))
                throw new ArgumentException("文件名不能为空");
            var enumerable = list as T[] ?? list.ToArray();
            if (!enumerable.Any())
                throw new ArgumentException("数据列表中没有数据");
            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
            }
            var ms = GetExcelMemoryFromList(list, headMsg);
            using (var fs = new FileStream(string.Format("{0}{1}", dirPath, fileName), FileMode.Create))
            {
                var data = ms.ToArray();
                fs.Write(data, 0, data.Length);
                fs.Flush();
            }
        }

        #endregion 导出

        #region 初始化

        /// <summary>
        /// 判断是否为兼容模式
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        private static bool GetIsCompatible(string filePath)
        {
            return filePath.EndsWith(".xls", StringComparison.OrdinalIgnoreCase);
        }

        /// <summary>
        /// Dt转List
        /// </summary>
        /// <param name="dataTable"></param>
        /// <returns></returns>
        private IEnumerable<T> PopularListFromDt(DataTable dataTable)
        {
            foreach (DataRow row in dataTable.Rows)
            {
                T t = Activator.CreateInstance<T>();

                foreach (KeyValuePair<string, PropertyInfo> info in _propertyInfos)
                {
                    if (dataTable.Columns.Contains(info.Key) && !string.IsNullOrEmpty(row[info.Key].ToString()))
                    {
                        info.Value.SetValue(t, ChangeType(row[info.Key], info.Value.PropertyType), null);
                    }
                }

                yield return t;
            }
            yield break;
        }

        /// <summary>
        /// 类型转换
        /// </summary>
        /// <param name="value"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        private object ChangeType(object value, Type type)
        {
            if (type == typeof(System.Enum) || type.IsEnum)
            {
                if (value is string)
                    return Enum.Parse(type, value as string);
                else
                    return Enum.ToObject(type, value);
            }
            return Convert.ChangeType(value, type);
        }

        private void BuildTableSchma<T>(T exportDto)
        {
            System.Reflection.PropertyInfo[] propertys = exportDto.GetType().GetProperties();
        }

        /// <summary>
        /// 初始化
        /// </summary>
        private void BuildTableSchma()
        {
            _dt = new DataTable();
            _propertyInfos = new Dictionary<string, PropertyInfo>();

            foreach (PropertyInfo info in typeof(T).GetProperties())
            {
                ExcelTitleAttribute attr = info.GetCustomAttributes(typeof(ExcelTitleAttribute), true)
                                            .FirstOrDefault() as ExcelTitleAttribute;

                if (attr == null) continue;
                if (_propertyInfos.ContainsKey(attr.Title))
                    throw new ArgumentException("Title is Exits!");

                _propertyInfos.Add(attr.Title, info);
                _dt.Columns.Add(attr.Title);
            }
        }

        #endregion 初始化
    }
}